package com.andova.net

import android.app.Dialog
import android.content.Context
import com.andova.component.AndovaComponentManager.getInstance
import com.andova.component.net.NetworkSusCallback2
import com.andova.component.net.ResponseModel
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import okhttp3.Request
import okio.Buffer
import retrofit2.Call
import retrofit2.Response
import java.io.IOException
import java.io.InputStream
import java.lang.ref.WeakReference
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.nio.charset.Charset

abstract class NetworkSusCallback2Impl<T : ResponseModel> : NetworkSusCallback2<T> {
    private var page = 1
    private var weakRefCxt: WeakReference<Context>? = null
    fun call(context: WeakReference<Context>, call: Call<T>) {
        weakRefCxt = context
        getInstance().netSusComponent().netSuspend2().request(context, this@NetworkSusCallback2Impl, call)
    }

    private fun stream(stream: InputStream): String {
        val buffer = ByteArray(128)
        var len: Int
        val sb = StringBuilder()
        try {
            while (stream.read(buffer).also { len = it } != -1) sb.append(String(buffer, 0, len))
            stream.close()
        } catch (e: IOException) {
        }
        return sb.toString()
    }

    private fun convertErrorBody(response: Response<*>): T? = response.body() as? T
    private fun <T> convertErrorBody(response: Response<*>, obj: Class<T>): T? {
        var `is`: InputStream? = null
        try {
            `is` = response.errorBody()?.byteStream() ?: return null
            val data = stream(`is`)
            return Gson().fromJson<T>(data, obj)
        } catch (e: Exception) {
        } finally {
            try {
                `is`?.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
        return null
    }

    override suspend fun prepare(context: WeakReference<Context>) {
        if (!showDialog() || page != 1) return
        dialog()?.show()
    }

    override suspend fun error(context: WeakReference<Context>, obj: Any?) {
        if (obj !is Response<*>) return
        try {
            val body = convertErrorBody(obj)
            printLog("$obj, error message -> ${body?.message ?: "unknown"}", true)
            handle(body ?: return)
        } catch (e: ClassCastException) {
            printResponse(obj.raw())
        } catch (e: NullPointerException) {
            printResponse(obj.raw())
        }
    }

    override suspend fun success(context: WeakReference<Context>, respBody: T) {
        ++page
        handle(respBody)
    }

    override suspend fun response(context: WeakReference<Context>, vararg objects: Any?) {
        try {
            if (dialog() != null) dialog()?.dismiss()
        } catch (e: IllegalArgumentException) {
            e.printStackTrace()
        }
        for (obj in objects) {
            if (obj !is Response<*>) continue
            printRequest(obj.raw().request())
        }
    }

    override suspend fun failure(context: WeakReference<Context>, throwable: Throwable?, vararg objects: Any?) {
        try {
            if (dialog() != null) dialog()?.dismiss()
        } catch (e: IllegalArgumentException) {
            e.printStackTrace()
        }
        var req: String? = null
        for (obj in objects) {
            if (obj !is Call<*>) continue
            req = getRequest(obj.request())
        }
        when (throwable) {
            is JsonSyntaxException -> printLog("$req, error message -> 数据解析出错", true)
            is SocketTimeoutException -> printLog("$req, error message -> 连接超时", true)
            is ConnectException -> printLog("$req, error message -> 连接服务器异常", true)
            else -> printLog("$req, unknown throwable -> $throwable", true)
        }
    }

    private fun getRequest(request: Request): String? {
        try {
            val buffer = Buffer()
            request.body()?.writeTo(buffer)
            val charset = Charset.forName("UTF-8")
            val body = buffer.readString(charset)
            return if (body.isEmpty()) request.toString() else "$request\nthe request body is:$body"
        } catch (e: IOException) {
            printLog("the request body is:$request", true)
        } catch (e: NullPointerException) {
            printLog("the request body is:$request", true)
        }
        return null
    }

    private fun printRequest(request: Request) {
        if (!editLog()) return
        printLog(getRequest(request) ?: return)
    }

    private fun printResponse(response: okhttp3.Response) {
        if (!editLog()) return
        try {
            printLog(response.body()?.string() ?: return, true)
        } catch (e: NullPointerException) {
            printLog(e, true)
        } catch (e: IOException) {
            printLog(e, true)
        }
    }

    fun page(): Int = page
    fun reset() = run { page = 1 }
    open fun toast(): Boolean = false
    open fun dialog(): Dialog? = null
    open fun editLog(): Boolean = true
    open fun showDialog(): Boolean = false
    protected abstract suspend fun handle(respBody: T)
    protected abstract fun log(msg: Any, error: Boolean)
    private fun printLog(log: Any, error: Boolean = false) {
        if (!editLog()) return
        when (log) {
            is String -> log(log, error)
            is Throwable -> log(log, error)
        }
    }
}